﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using GRIP.Cuda;
using System.Runtime.InteropServices;

namespace GRIP
{
    public class CudaImageProcessor : IKinectImageProcessor
    {
        CudaHostBuffer ColorFrameHost;
        CudaHostBuffer DepthFrameHost;
        CudaDeviceBuffer ColorFrameDevice;
        CudaDeviceBuffer ColorFrameDevice2;
        CudaDeviceBuffer DepthFrameDevice;
        CudaDeviceBuffer AlphaChannel;
        ColorAlignmentMode ColorAlignmentMode;

        public CudaImageProcessor(KinectSettings Settings)
        {
            switch (Settings.ColorMode)
            {
                case ColorMode.Off:
                    break;
                case ColorMode.Rgb_1280x960_12FPS:
                    ColorFrameHost = new CudaHostBuffer(1280, 960, 4);
                    ColorFrameDevice = new CudaDeviceBuffer(1280, 960, 4);
                    ColorFrameDevice2 = new CudaDeviceBuffer(1280, 960, 4);
                    break;
                case ColorMode.Rgb_640x480_30FPS:
                    ColorFrameHost = new CudaHostBuffer(640, 480, 4);
                    ColorFrameDevice = new CudaDeviceBuffer(640, 480, 4);
                    ColorFrameDevice2 = new CudaDeviceBuffer(640, 480, 4);
                    break;
                case ColorMode.YuvRaw_640x480_15FPS:
                    ColorFrameHost = new CudaHostBuffer(640, 480, 2);
                    ColorFrameDevice = new CudaDeviceBuffer(640, 480, 2);
                    ColorFrameDevice2 = new CudaDeviceBuffer(640, 480, 2);
                    break;
                case ColorMode.YuvToRgb_640x480_15FPS:
                    ColorFrameHost = new CudaHostBuffer(640, 480, 4);
                    ColorFrameDevice = new CudaDeviceBuffer(640, 480, 4);
                    ColorFrameDevice2 = new CudaDeviceBuffer(640, 480, 4);
                    break;
            }

            switch (Settings.DepthMode)
            {
                case DepthMode.Off:
                    break;
                case DepthMode.On_320x240_30FPS:
                    DepthFrameHost = new CudaHostBuffer(320, 240, 2);
                    DepthFrameDevice = new CudaDeviceBuffer(320, 240, 2);
                    break;
                case DepthMode.On_640x480_30FPS:
                    DepthFrameHost = new CudaHostBuffer(640, 480, 2);
                    DepthFrameDevice = new CudaDeviceBuffer(640, 480, 2);
                    break;
                case DepthMode.On_80x60_30FPS:
                    DepthFrameHost = new CudaHostBuffer(80, 60, 2);
                    DepthFrameDevice = new CudaDeviceBuffer(80, 60, 2);
                    break;
            }

            switch (Settings.BackgroundSubtractionMode)
            {
                case BackgroundSubtractionMode.AllPeople:
                    break;
                case BackgroundSubtractionMode.AllPeopleWithAlpha:
                    AlphaChannel = new CudaDeviceBuffer(640, 480, 1);
                    break;
                case BackgroundSubtractionMode.ClosestPerson:
                    break;
                case BackgroundSubtractionMode.ClosestPersonWithAlpha:
                    AlphaChannel = new CudaDeviceBuffer(640, 480, 1);
                    break;
                case BackgroundSubtractionMode.Off:
                    break;
            }

            ColorAlignmentMode = Settings.ColorAlignmentMode;
        }

        public void SetAlignmentMap(int[] AlignmentMap)
        {
            GripGpu.SetMapIndices(AlignmentMap);
        }

        public void HandleNewColorImageFrame(ColorImageFrame Frame)
        {
            try
            {
                Frame.CopyPixelDataTo(ColorFrameHost.Data(), Frame.PixelDataLength);
                if (ColorAlignmentMode == GRIP.ColorAlignmentMode.Off)
                    ColorFrameHost.CopyToDevice(ColorFrameDevice);
                else
                    ColorFrameHost.CopyToDevice(ColorFrameDevice2);
            }
            catch (AccessViolationException e)
            {
                throw e;
            }
        }
        public void HandleNewDepthImageFrame(DepthImageFrame Frame)
        {
            try
            {
                Frame.CopyPixelDataTo(DepthFrameHost.Data(), Frame.PixelDataLength);
                DepthFrameHost.CopyToDevice(DepthFrameDevice);
            }
            catch (AccessViolationException e)
            {
                throw e;
            }
        }

        public void ClearFrames()
        {
            if (ColorAlignmentMode == GRIP.ColorAlignmentMode.Off)
            {
                //byte[] bytes = new byte[ColorFrameDevice.BufferSize()];
                //Marshal.Copy(bytes, 0, ColorFrameHost.Data(), bytes.Length);
                //ColorFrameHost.CopyToDevice(ColorFrameDevice);
                ColorFrameDevice.ClearData();
            }
            else
            {
                //byte[] bytes = new byte[ColorFrameDevice2.BufferSize()];
                //Marshal.Copy(bytes, 0, ColorFrameHost.Data(), bytes.Length);
                //ColorFrameHost.CopyToDevice(ColorFrameDevice2);
                ColorFrameDevice2.ClearData();
            }
            //byte[] bytes2 = new byte[DepthFrameDevice.BufferSize()];
            //Marshal.Copy(bytes2, 0, DepthFrameHost.Data(), bytes2.Length);
            //DepthFrameHost.CopyToDevice(DepthFrameDevice);
            DepthFrameDevice.ClearData();
            AlphaChannel.ClearData();
        }

        public void AlignColorFrame()
        {
            lock (ColorFrameDevice)
            {
                GripGpu.AlignColorFrame(ColorFrameDevice2, ColorFrameDevice);
            }
        }

        public void BackgroundSubtract(int PlayerIndex)
        {
            lock (AlphaChannel)
            {
                GripGpu.BackgroundSubtract(DepthFrameDevice, AlphaChannel, PlayerIndex);
            }
        }

        public void GetProcessedDepthFrame(short[] DepthFrame)
        {
            CudaManager.CopyBytesFromDevice(DepthFrameDevice, DepthFrame);
        }

        public CudaDeviceBuffer GetAlphaChannel()
        {
            return AlphaChannel;
        }

        public CudaDeviceBuffer GetColorBuffer()
        {
            return ColorFrameDevice;
        }

        public CudaDeviceBuffer GetDepthBuffer()
        {
            return DepthFrameDevice;
        }
    }
}
